package cloudspace.util.jassistwrappers;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cloudspace.util.WeakIdentityHashMap;

/**
 * The Class VMFile. This file represents all of the static methods used to wrap
 * and rewrite file calls. This wrapper implements a Map from files to user path
 * names for retrieval later. It inherits from {@link VMClassContext} to allow
 * access to local path information.
 * 
 * It is important to note that all files created in any form must be logged.
 * This includes files created from api calls. If every file created is not
 * logged, then the entire system will fail.
 */
public class VMFile extends VMClassContext
{

	/** The user passed path map. */
	static private WeakIdentityHashMap<File, String> userPassedPathMap = new WeakIdentityHashMap<File, String>();

	/**
	 * This function logs a list of files created from the File.list functions.
	 * 
	 * @param parent
	 *            the parent file that the list of file was created using
	 * @param list
	 *            the list of files attached to the parent file
	 */
	public static void logFileList(File parent, File[] list)
	{
		if (list != null)
		{
			for (int i = 0; i < list.length; i++)
			{
				File elem = list[i];
				if (getUserPassedString(elem) == null)
				{
					logFileWithParent(parent, elem);
				}
			}
		}
	}

	/**
	 * Log a file created from a parent file.
	 * 
	 * @param parent
	 *            the parent file
	 * @param elem
	 *            the newly created file
	 */
	public static void logFileWithParent(File parent, File elem)
	{
		String prefix = getCurrentPrefix();
		boolean abs = isFileAbsolute(parent);
		if (abs)
		{
			logUserPassedString(elem, elem.getAbsolutePath().substring(
					prefix.length() - 1));
		}
		else
		{
			String tempShort = elem.getPath().substring(prefix.length());
			logUserPassedString(elem, tempShort);
		}
	}

	/**
	 * Log a file according to a path passed to new File from a user
	 * 
	 * @param newFile
	 *            the newly created file
	 * @param userPath
	 *            the path passed to new File from the user.
	 */
	public static void logUserPassedString(File newFile, String userPath)
	{
		userPassedPathMap.put(newFile, userPath);
	}

	/**
	 * using a file as a reference, the path used to create a file is returned.
	 * The path returned might not have been passed by the user. It might have
	 * been created on the fly by VMFile according to a parent file or some
	 * other heuristics.
	 * 
	 * @param lookupFile
	 *            the file to lookup
	 * 
	 * @return the string used to instanciate the file.
	 */
	public static String getUserPassedString(File lookupFile)
	{
		String s = userPassedPathMap.get(lookupFile);
		
		return s;
	}

	/**
	 * Using a file name, VMFile will translate the filename to an absolute file
	 * name according to the proper location in the filesystem.
	 * 
	 * @param fname
	 *            The file name to translate to an absolute file name
	 * 
	 * @return the true absolute filename
	 */
	public static String toGlobalName(String fname)
	{
		String prefix = getCurrentPrefix();
		fname = stripRoot(fname);
		fname = cleanWindowsPath(fname);
		if (fname != null)
		{
			return prefix + fname;
		}
		else
		{
			return fname;
		}
	}

	private static String cleanWindowsPath(String path)
	{
		return path.replace('\\', '/');
	}

	public static String toRawGlobalName(String fname)
	{
		return getCurrentPrefix() + fname;
	}

	private static String stripRoot(String path)
	{
		if (hasWindowsDriveLetter(path))
		{
			return path.substring(3);
		}
		if (path.startsWith("/"))
		{
			return path.substring(1);
		}
		return path;
	}

	/**
	 * This function checks if the file created by the user is actually
	 * absolute. This looks up the user passed string in an attempt to reliably
	 * provide information about absoluteness of the original file.
	 * 
	 * @param testFile
	 *            The file to check for absoluteness
	 * 
	 * @return true, if the file is absolute
	 */
	public static boolean isFileAbsolute(File testFile)
	{
		String userPassed = getUserPassedString(testFile);
		return isAbsolute(userPassed);
	}

	/**
	 * This function checks a path for absoluteness. Specifically, this function
	 * looks for a path that either starts with / or /[a-zA-Z]:/
	 * 
	 * @param path
	 *            the to check for absoluteness
	 * 
	 * @return true, if is absolute
	 */
	public static boolean isAbsolute(String path)
	{

		if (path.startsWith("/"))
		{
			return true;
		}
		return isWindowsAbsolute(path);
	}

	private static boolean isWindowsAbsolute(String path)
	{
		Pattern p = Pattern.compile("[a-zA-Z]://.*");
		Matcher m = p.matcher(path);
		return m.matches();
	}

	private static boolean hasWindowsDriveLetter(String path)
	{
		Pattern p = Pattern.compile("[a-zA-Z]:/.*");
		Matcher m = p.matcher(path);
		return m.matches();
	}

	/**
	 * This function returns the user local name of a string according to a
	 * file. In other words, this string is what should be presented to the user
	 * when the user has a stirng produced from a File.
	 * 
	 * @param curFile
	 *            the file to check
	 * @param fname
	 *            the file name that is related to the curFile
	 * 
	 * @return the local name of the path that should be presented to the user.
	 */
	public static String toLocalName(File curFile, String fname)
	{
		if (fname == null)
			return fname;
		String prefix = getCurrentPrefix();

		String userPassed = getUserPassedString(curFile);
		if (userPassed == null)
		{
			System.out.println("could not find " + curFile);
			return fname;
		}
		if (userPassed.equals(""))
		{
			return "";
		}
		if (userPassed.equals("/"))
		{
			return "/";
		}
		fname = fname.substring(prefix.length());
		//TODO: This could be flawed if a student has overwritten getPath() this will be broken
		if (isAbsolute(userPassed) && !fname.startsWith("/"))
		{
			fname = "/" + fname;
		}

		return fname;
	}

	/**
	 * This returns the local absolute name of a file. Meaning if a user asked
	 * for the absolute name of a file, the should expect to see this value
	 * 
	 * @param fname
	 *            the path to be converted
	 * 
	 * @return absolute path as seen by the user
	 */
	public static String toLocalAbsName(File curFile, String fname)
	{
		String userPassed = getUserPassedString(curFile);
		String prefix = getCurrentPrefix();
		if (fname == null)
		{
			return null;
		}
		if (userPassed.equals(""))
		{
			return "/";
		}
		String cleaned = fname.substring(prefix.length());
		if (!cleaned.startsWith("/") || !isWindowsAbsolute(cleaned))
		{
			cleaned = "/" + cleaned;
		}
		return cleaned;
	}

	/**
	 * This function checks for the equality of two files
	 * 
	 * @param file1
	 *            the first file
	 * @param file2
	 *            the second file
	 * 
	 * @return the integer representing equality.
	 */
	public static int compareFiles(File file1, File file2)
	{
		File userFile1 = new File(getUserPassedString(file1));
		File userFile2 = new File(getUserPassedString(file2));

		return userFile1.compareTo(userFile2);
	}

	/**
	 * This is a convinence function for logging a file that was produced from
	 * an api call.
	 * 
	 * @param apiFile
	 *            the File produced from an api call.
	 */
	public static void logNewAPIFile(File apiFile)
	{
		String prefix = getCurrentPrefix();
		if (getUserPassedString(apiFile) == null)
			userPassedPathMap.put(apiFile, apiFile.getAbsolutePath().substring(
					prefix.length() - 1));
	}

	public static File[] cleanFileList(File[] fileList)
	{
		File[] files = new File[fileList.length];
		int index = 0;
		for (File myFile : fileList)
		{
			String userPassed = getUserPassedString(myFile);
			if (userPassed != null)
			{
				files[index] = myFile;
			}
			else
			{
				File cleanedFile = new File(toGlobalName(myFile.getPath()));
				logUserPassedString(cleanedFile, myFile.getPath());
				files[index] = cleanedFile;
			}
		}
		return files;
	}

	public static String fixGetName(String origReturnValue, File f)
	{
		if (f == null)
			return origReturnValue;
		String userpassed = getUserPassedString(f);
		if (userpassed.equals("/") || userpassed.equals(""))
		{
			return "";
		}
		return origReturnValue;
	}

	public static String getParent(File myFile)
	{
		String parentPath = myFile.getParent();
		if (myFile == null)
			return parentPath;
		String userpassed = getUserPassedString(myFile);
		if (userpassed.equals(""))
		{
			return null;
		}
		if (userpassed.equals("/"))
		{
			return null;
		}
		// if (myFile.getName().equals(getCurrentPrefix()))
		// {
		// return null;
		// }
		if (parentPath.equals(getCurrentPrefix().substring(0,
				getCurrentPrefix().length() - 1)))
			return null;
		return toLocalName(myFile, parentPath);
	}

	public static File getParentFile(File myFile, File parentFile)
	{
		if (parentFile == null)
			return null;
		String parentPath = getParent(myFile);
		if (parentPath == null)
		{
			return null;
		}
		logUserPassedString(parentFile, parentPath);
		return parentFile;
	}

	public static String[] cleanPaths(File f, String[] paths)
	{
		if(paths == null)
		{
			return paths;
		}
		String[] newPath = new String[paths.length];
		String userpassed = getUserPassedString(f);
		boolean isAbsolute = isAbsolute(userpassed);
		int index = 0;
		for (String path : paths)
		{
			path = path.substring(getCurrentPrefix().length());
			if (isAbsolute)
				path = "/" + path;
			newPath[index] = path;
			index++;
		}
		return newPath;
	}

	public static String[] globalizePaths(String[] paths)
	{
		for (int i = 0; i < paths.length; i++)
		{
			paths[i] = getCurrentPrefix() + paths[i];
		}
		return paths;
	}
}